package cn.yeziji.forum.utils;

import cn.yeziji.forum.exception.TokenException;
import cn.yeziji.forum.common.enums.status.TokenStatus;
import cn.yzj.utils.DataUtils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.*;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.*;

/**
 * Jwt 操作工具类
 *
 * @author gzkemays
 * @since 2021/12/22 17:37
 */
public class JwtOperaUtils {
  private static final String SUBJECT = "ES0Q2MwCbcj9";
  private static final String SECURITY = "1aMNZ4MG1wHMJ23";
  private static final JwtUtils JWT = JwtUtils.settings().init(SUBJECT, SECURITY);
  private static final String USER_NAME_KEY = "username";
  private static final String NICK_NAME_KEY = "nickname";
  private static final String ID_KEY = "id";
  private static final String ROLES = "roles";
  /** 有效时间为 7 天 */
  private static final long EXPIRED = 7 * 1000 * 60 * 60 * 24;
  //  private static final long EXPIRED = 1000 * 30;

  public static Integer getUserIdIfZero(HttpServletRequest request) {
    String token = request.getHeader(HttpHeaders.AUTHORIZATION);
    if (Objects.isNull(token)) {
      return 0;
    }
    return getUserIdByRequest(request);
  }

  public static boolean hasToken(HttpServletRequest request) {
    String token = request.getHeader(HttpHeaders.AUTHORIZATION);
    return !Objects.isNull(token);
  }

  /**
   * 获取 request 中的 token
   *
   * @param request request
   * @return token
   */
  public static String getTokenByRequest(HttpServletRequest request) {
    String token = request.getHeader(HttpHeaders.AUTHORIZATION);
    if (Objects.isNull(token) || !StringUtils.hasText(token)) {
      throw new TokenException(TokenStatus.TOKEN_IS_NULL);
    }
    return token;
  }

  /**
   * 生产 token
   *
   * @param id id
   * @param username 用户名
   * @return token
   */
  public static String generateJsonWebToken(Integer id, String username, Object authorities) {
    return Jwts.builder()
        .setSubject(SUBJECT)
        .claim(ROLES, authorities)
        .claim(ID_KEY, id)
        .claim(USER_NAME_KEY, username)
        .setIssuedAt(new Date())
        .setExpiration(new Date(System.currentTimeMillis() + EXPIRED))
        .signWith(SignatureAlgorithm.HS256, SECURITY)
        .compact();
  }

  /**
   * 根据传入的实体类加入非 null 属性
   *
   * @param data 实体类
   * @param <T> 泛型
   * @return token
   */
  public static <T> String generateJsonWebTokenByPoJo(T data) {
    JwtBuilder jwt =
        Jwts.builder().setSubject(SUBJECT).signWith(SignatureAlgorithm.HS256, SECURITY);
    Class<?> clazz = data.getClass();
    Field[] fields = clazz.getDeclaredFields();
    Map<String, Object> map = new HashMap<>(fields.length);
    for (Field field : fields) {
      field.setAccessible(true);
      try {
        Object obj = field.get(data);
        if (obj != null) {
          map.put(field.getName(), obj);
        }
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      }
    }
    jwt.setClaims(map);
    jwt.setIssuedAt(new Date());
    jwt.setExpiration(new Date(System.currentTimeMillis() + EXPIRED));
    return jwt.compact();
  }

  public static String generateJsonWebTokenByJson(JSONObject data) {
    JwtBuilder jwt =
        Jwts.builder()
            .setSubject(SUBJECT)
            .setClaims(data)
            // 签发时间
            .setIssuedAt(new Date())
            // 有效时间
            .setExpiration(new Date(System.currentTimeMillis() + EXPIRED))
            // 加密
            .signWith(SignatureAlgorithm.HS256, SECURITY);
    return jwt.compact();
  }

  /**
   * 获取 token - username
   *
   * @param request request
   * @return username
   */
  public static String getUserNameByRequest(HttpServletRequest request) {
    return getTokenClaimByRequest(request, USER_NAME_KEY, String.class);
  }

  public static String getUserNameByToken(String token) {
    return getTokenClaimByToken(token, USER_NAME_KEY, String.class);
  }

  /**
   * 获取 token - nickname
   *
   * @param request request
   * @return nickname
   */
  public static String getNickNameByRequest(HttpServletRequest request) {
    return getTokenClaimByRequest(request, NICK_NAME_KEY, String.class);
  }

  public static String getNickNameNameByToken(String token) {
    return getTokenClaimByToken(token, NICK_NAME_KEY, String.class);
  }

  public static Integer getIdByToken(String token) {
    return getTokenClaimByToken(token, ID_KEY, Integer.class);
  }

  /**
   * 获取 token - id
   *
   * @param request request
   * @return id
   */
  public static Integer getUserIdByRequest(HttpServletRequest request) {
    return getTokenClaimByRequest(request, ID_KEY, Integer.class);
  }

  @SuppressWarnings("all")
  public static List<SimpleGrantedAuthority> getUserRole(String token) {
    Claims claims = JWT.checkJwt(token);
    if (claims == null) {
      return new ArrayList<>();
    }
    List roles = (List) claims.get(ROLES);
    String json = JSONArray.toJSONString(roles);
    List<SimpleGrantedAuthority> grantedAuthorityList =
        JSONArray.parseArray(json, SimpleGrantedAuthority.class);
    return grantedAuthorityList;
  }
  /**
   * 根据 key 返回指定 clazz 泛型数据
   *
   * @param request request
   * @param key 键值
   * @param clazz 泛型类
   * @param <T> 返回泛型
   * @return 泛型数据
   */
  @SuppressWarnings("unchecked")
  public static <T> T getTokenClaimByRequest(
      HttpServletRequest request, String key, Class<T> clazz) {
    String token = request.getHeader(HttpHeaders.AUTHORIZATION);
    if (StringUtils.hasText(token)) {
      Claims claims = JWT.checkJwt(token);
      if (claims != null) {
        return (T) claims.get(key);
      }
      throw new TokenException(TokenStatus.TOKEN_INVALID);
    }
    throw new TokenException(TokenStatus.TOKEN_IS_NULL);
  }

  public static boolean checkToken(String token) {
    Claims claims = JWT.checkJwt(token);
    return claims != null;
  }

  public static boolean isExpired(String token) {
    try {
      return JWT.isExpiration(token);
    } catch (ExpiredJwtException expired) {
      return true;
    }
  }

  @SuppressWarnings("unchecked")
  public static <T> T getTokenClaimByToken(String token, String key, Class<T> clazz) {
    if (StringUtils.hasText(token)) {
      Claims claims = JWT.checkJwt(token);
      return claims == null ? null : (T) claims.get(key);
    }
    throw new TokenException(TokenStatus.TOKEN_IS_NULL);
  }

  public static <T> T getTokenClaimByToken(String token, Class<T> clazz) {
    if (StringUtils.hasText(token)) {
      Claims claims = JWT.checkJwt(token);
      return DataUtils.getObjectFromMap(claims, clazz);
    }
    throw new TokenException(TokenStatus.TOKEN_IS_NULL);
  }
}
